#pragma once
#include"nocopy.hpp"
#include<sys/types.h>
#include<sys/socket.h>
#include<cerrno>
#include<vector>
#include<functional>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include"Log.hpp"
#include"InetAddr.hpp"
#include"Thread.hpp"
#include"Log.hpp"
#include"threadpool.hpp"

enum{
    SOCKET_ERR = 1,
    BIND_ERR
};

using task_t = function<void()>;

class UdpServer: public nocopy
{
public:
    UdpServer(uint16_t port)
        :_port(port),_sockfd(-1)
    {
        pthread_mutex_init(&_mutex,nullptr);
    }

    void Init()
    {
        _sockfd = socket(AF_INET,SOCK_DGRAM,0);
        if(_sockfd < 0)
        {
            log.LogMessage(Fatal,"socket err %d %s\n",errno,strerror(errno));
            exit(SOCKET_ERR);
        }

        log.LogMessage(Debug,"socket success sockfd: %d\n",_sockfd);

        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        socklen_t len = sizeof(local);
        int n = ::bind(_sockfd, (struct sockaddr*)&local,len);
        if(n != 0)
        {
            log.LogMessage(Fatal,"bind err %d %s",errno,strerror(errno));
            exit(BIND_ERR);
        }

        log.LogMessage(Debug,"bind success");
        threadpool<task_t>::GetInsTance()->Start();
    }

    void add_addr(inetaddr& addr)
    {
        LockGuard lock(&_mutex);
        for(auto& user : _user_addr)
        {
            if(addr == user)
            {
                return;
            }
        }
        _user_addr.push_back(addr);
        log.LogMessage(Debug,"add success");
    }

    void Task(const string& message)
    {
        LockGuard lock(&_mutex);
        for(auto& e: _user_addr)
        {
            sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&(e.GetAddr()),sizeof(e.GetAddr()));
        }
        log.LogMessage(Debug,"send success");
    }

    void Start()
    {
        char receive[1024];
        
        while(true)
        {
            struct sockaddr_in local;
            socklen_t size = sizeof(local);
            ssize_t n =  recvfrom(_sockfd, receive, sizeof(receive)-1,0, (struct sockaddr*)&local,&size);
            if(n  > 0)
            {   
                inetaddr addr(local);
                receive[n] = 0;
                add_addr(addr);
                string _send = "[";
                _send += addr.iP();
                _send += ":";
                _send += to_string(addr.port());
                _send += "]#";
                _send += receive; 
                auto task = bind(&UdpServer::Task,this,_send);
                threadpool<task_t>::GetInsTance()->Push(task);
            }

        }
    }

    ~UdpServer()
    {}

private:
    uint16_t _port;
    int _sockfd;
    vector<inetaddr> _user_addr;

    pthread_mutex_t _mutex;
    task_t _task;
    
};